home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Linux / Kubuntu 8.10 / kubuntu-8.10-desktop-i386.iso / casper / filesystem.squashfs / usr / share / pyshared / PIL / GifImagePlugin.py < prev    next >
Text File  |  2006-12-03  |  11KB  |  406 lines

  1. #
  2. # The Python Imaging Library.
  3. # $Id: GifImagePlugin.py 2134 2004-10-06 08:55:20Z fredrik $
  4. #
  5. # GIF file handling
  6. #
  7. # History:
  8. # 1995-09-01 fl   Created
  9. # 1996-12-14 fl   Added interlace support
  10. # 1996-12-30 fl   Added animation support
  11. # 1997-01-05 fl   Added write support, fixed local colour map bug
  12. # 1997-02-23 fl   Make sure to load raster data in getdata()
  13. # 1997-07-05 fl   Support external decoder (0.4)
  14. # 1998-07-09 fl   Handle all modes when saving (0.5)
  15. # 1998-07-15 fl   Renamed offset attribute to avoid name clash
  16. # 2001-04-16 fl   Added rewind support (seek to frame 0) (0.6)
  17. # 2001-04-17 fl   Added palette optimization (0.7)
  18. # 2002-06-06 fl   Added transparency support for save (0.8)
  19. # 2004-02-24 fl   Disable interlacing for small images
  20. #
  21. # Copyright (c) 1997-2004 by Secret Labs AB
  22. # Copyright (c) 1995-2004 by Fredrik Lundh
  23. #
  24. # See the README file for information on usage and redistribution.
  25. #
  26.  
  27.  
  28. __version__ = "0.9"
  29.  
  30.  
  31. import Image, ImageFile, ImagePalette
  32.  
  33.  
  34. # --------------------------------------------------------------------
  35. # Helpers
  36.  
  37. def i16(c):
  38.     return ord(c[0]) + (ord(c[1])<<8)
  39.  
  40. def o16(i):
  41.     return chr(i&255) + chr(i>>8&255)
  42.  
  43.  
  44. # --------------------------------------------------------------------
  45. # Identify/read GIF files
  46.  
  47. def _accept(prefix):
  48.     return prefix[:6] in ["GIF87a", "GIF89a"]
  49.  
  50. ##
  51. # Image plugin for GIF images.  This plugin supports both GIF87 and
  52. # GIF89 images.
  53.  
  54. class GifImageFile(ImageFile.ImageFile):
  55.  
  56.     format = "GIF"
  57.     format_description = "Compuserve GIF"
  58.  
  59.     global_palette = None
  60.  
  61.     def data(self):
  62.         s = self.fp.read(1)
  63.         if s and ord(s):
  64.             return self.fp.read(ord(s))
  65.         return None
  66.  
  67.     def _open(self):
  68.  
  69.         # Screen
  70.         s = self.fp.read(13)
  71.         if s[:6] not in ["GIF87a", "GIF89a"]:
  72.             raise SyntaxError, "not a GIF file"
  73.  
  74.         self.info["version"] = s[:6]
  75.  
  76.         self.size = i16(s[6:]), i16(s[8:])
  77.  
  78.         self.tile = []
  79.  
  80.         flags = ord(s[10])
  81.  
  82.         bits = (flags & 7) + 1
  83.  
  84.         if flags & 128:
  85.             # get global palette
  86.             self.info["background"] = ord(s[11])
  87.             # check if palette contains colour indices
  88.             p = self.fp.read(3<<bits)
  89.             for i in range(0, len(p), 3):
  90.                 if not (chr(i/3) == p[i] == p[i+1] == p[i+2]):
  91.                     p = ImagePalette.raw("RGB", p)
  92.                     self.global_palette = self.palette = p
  93.                     break
  94.  
  95.         self.__fp = self.fp # FIXME: hack
  96.         self.__rewind = self.fp.tell()
  97.         self.seek(0) # get ready to read first frame
  98.  
  99.     def seek(self, frame):
  100.  
  101.         if frame == 0:
  102.             # rewind
  103.             self.__offset = 0
  104.             self.dispose = None
  105.             self.__frame = -1
  106.             self.__fp.seek(self.__rewind)
  107.  
  108.         if frame != self.__frame + 1:
  109.             raise ValueError, "cannot seek to frame %d" % frame
  110.         self.__frame = frame
  111.  
  112.         self.tile = []
  113.  
  114.         self.fp = self.__fp
  115.         if self.__offset:
  116.             # backup to last frame
  117.             self.fp.seek(self.__offset)
  118.             while self.data():
  119.                 pass
  120.             self.__offset = 0
  121.  
  122.         if self.dispose:
  123.             self.im = self.dispose
  124.             self.dispose = None
  125.  
  126.         self.palette = self.global_palette
  127.  
  128.         while 1:
  129.  
  130.             s = self.fp.read(1)
  131.             if not s or s == ";":
  132.                 break
  133.  
  134.             elif s == "!":
  135.                 #
  136.                 # extensions
  137.                 #
  138.                 s = self.fp.read(1)
  139.                 block = self.data()
  140.                 if ord(s) == 249:
  141.                     #
  142.                     # graphic control extension
  143.                     #
  144.                     flags = ord(block[0])
  145.                     if flags & 1:
  146.                         self.info["transparency"] = ord(block[3])
  147.                     self.info["duration"] = i16(block[1:3]) * 10
  148.                     try:
  149.                         # disposal methods
  150.                         if flags & 8:
  151.                             # replace with background colour
  152.                             self.dispose = Image.core.fill("P", self.size,
  153.                                 self.info["background"])
  154.                         elif flags & 16:
  155.                             # replace with previous contents
  156.                             self.dispose = self.im.copy()
  157.                     except (AttributeError, KeyError):
  158.                         pass
  159.                 elif ord(s) == 255:
  160.                     #
  161.                     # application extension
  162.                     #
  163.                     self.info["extension"] = block, self.fp.tell()
  164.                     if block[:11] == "NETSCAPE2.0":
  165.                         self.info["loop"] = 1 # FIXME
  166.                 while self.data():
  167.                     pass
  168.  
  169.             elif s == ",":
  170.                 #
  171.                 # local image
  172.                 #
  173.                 s = self.fp.read(9)
  174.  
  175.                 # extent
  176.                 x0, y0 = i16(s[0:]), i16(s[2:])
  177.                 x1, y1 = x0 + i16(s[4:]), y0 + i16(s[6:])
  178.                 flags = ord(s[8])
  179.  
  180.                 interlace = (flags & 64) != 0
  181.  
  182.                 if flags & 128:
  183.                     bits = (flags & 7) + 1
  184.                     self.palette =\
  185.                         ImagePalette.raw("RGB", self.fp.read(3<<bits))
  186.  
  187.                 # image data
  188.                 bits = ord(self.fp.read(1))
  189.                 self.__offset = self.fp.tell()
  190.                 self.tile = [("gif",
  191.                              (x0, y0, x1, y1),
  192.                              self.__offset,
  193.                              (bits, interlace))]
  194.                 break
  195.  
  196.             else:
  197.                 pass
  198.                 # raise IOError, "illegal GIF tag `%x`" % ord(s)
  199.  
  200.         if not self.tile:
  201.             # self.__fp = None
  202.             raise EOFError, "no more images in GIF file"
  203.  
  204.         self.mode = "L"
  205.         if self.palette:
  206.             self.mode = "P"
  207.  
  208.     def tell(self):
  209.         return self.__frame
  210.  
  211.  
  212. # --------------------------------------------------------------------
  213. # Write GIF files
  214.  
  215. try:
  216.     import _imaging_gif
  217. except ImportError:
  218.     _imaging_gif = None
  219.  
  220. RAWMODE = {
  221.     "1": "L",
  222.     "L": "L",
  223.     "P": "P",
  224. }
  225.  
  226. def _save(im, fp, filename):
  227.  
  228.     if _imaging_gif:
  229.         # call external driver
  230.         try:
  231.             _imaging_gif.save(im, fp, filename)
  232.             return
  233.         except IOError:
  234.             pass # write uncompressed file
  235.  
  236.     try:
  237.         rawmode = RAWMODE[im.mode]
  238.         imOut = im
  239.     except KeyError:
  240.         # convert on the fly (EXPERIMENTAL -- I'm not sure PIL
  241.         # should automatically convert images on save...)
  242.         if Image.getmodebase(im.mode) == "RGB":
  243.             imOut = im.convert("P")
  244.             rawmode = "P"
  245.         else:
  246.             imOut = im.convert("L")
  247.             rawmode = "L"
  248.  
  249.     # header
  250.     for s in getheader(imOut, im.encoderinfo):
  251.         fp.write(s)
  252.  
  253.     flags = 0
  254.  
  255.     try:
  256.         interlace = im.encoderinfo["interlace"]
  257.     except KeyError:
  258.         interlace = 1
  259.  
  260.     # workaround for @PIL153
  261.     if min(im.size) < 16:
  262.         interlace = 0
  263.  
  264.     if interlace:
  265.         flags = flags | 64
  266.  
  267.     try:
  268.         transparency = im.encoderinfo["transparency"]
  269.     except KeyError:
  270.         pass
  271.     else:
  272.         # transparency extension block
  273.         fp.write("!" +
  274.                  chr(249) +             # extension intro
  275.                  chr(4) +               # length
  276.                  chr(1) +               # transparency info present
  277.                  o16(0) +               # duration
  278.                  chr(int(transparency)) # transparency index
  279.                  + chr(0))
  280.  
  281.     # local image header
  282.     fp.write("," +
  283.              o16(0) + o16(0) +          # bounding box
  284.              o16(im.size[0]) +          # size
  285.              o16(im.size[1]) +
  286.              chr(flags) +               # flags
  287.              chr(8))                    # bits
  288.  
  289.     imOut.encoderconfig = (8, interlace)
  290.  
  291.     ImageFile._save(imOut, fp, [("gif", (0,0)+im.size, 0, rawmode)])
  292.  
  293.     fp.write("\0") # end of image data
  294.  
  295.     fp.write(";") # end of file
  296.  
  297.     try:
  298.         fp.flush()
  299.     except: pass
  300.  
  301. def _save_netpbm(im, fp, filename):
  302.  
  303.     #
  304.     # If you need real GIF compression and/or RGB quantization, you
  305.     # can use the external NETPBM/PBMPLUS utilities.  See comments
  306.     # below for information on how to enable this.
  307.  
  308.     import os
  309.     file = im._dump()
  310.     if im.mode != "RGB":
  311.         os.system("ppmtogif %s >%s" % (file, filename))
  312.     else:
  313.         os.system("ppmquant 256 %s | ppmtogif >%s" % (file, filename))
  314.     try: os.unlink(file)
  315.     except: pass
  316.  
  317.  
  318. # --------------------------------------------------------------------
  319. # GIF utilities
  320.  
  321. def getheader(im, info=None):
  322.     """Return a list of strings representing a GIF header"""
  323.  
  324.     optimize = info and info.get("optimize", 0)
  325.  
  326.     s = [
  327.         "GIF87a" +              # magic
  328.         o16(im.size[0]) +       # size
  329.         o16(im.size[1]) +
  330.         chr(7 + 128) +          # flags: bits + palette
  331.         chr(0) +                # background
  332.         chr(0)                  # reserved/aspect
  333.     ]
  334.  
  335.     if optimize:
  336.         # minimize color palette
  337.         i = 0
  338.         maxcolor = 0
  339.         for count in im.histogram():
  340.             if count:
  341.                 maxcolor = i
  342.             i = i + 1
  343.     else:
  344.         maxcolor = 256
  345.  
  346.     # global palette
  347.     if im.mode == "P":
  348.         # colour palette
  349.         s.append(im.im.getpalette("RGB")[:maxcolor*3])
  350.     else:
  351.         # greyscale
  352.         for i in range(maxcolor):
  353.             s.append(chr(i) * 3)
  354.  
  355.     return s
  356.  
  357. def getdata(im, offset = (0, 0), **params):
  358.     """Return a list of strings representing this image.
  359.        The first string is a local image header, the rest contains
  360.        encoded image data."""
  361.  
  362.     class collector:
  363.         data = []
  364.         def write(self, data):
  365.             self.data.append(data)
  366.  
  367.     im.load() # make sure raster data is available
  368.  
  369.     fp = collector()
  370.  
  371.     try:
  372.         im.encoderinfo = params
  373.  
  374.         # local image header
  375.         fp.write("," +
  376.                  o16(offset[0]) +       # offset
  377.                  o16(offset[1]) +
  378.                  o16(im.size[0]) +      # size
  379.                  o16(im.size[1]) +
  380.                  chr(0) +               # flags
  381.                  chr(8))                # bits
  382.  
  383.         ImageFile._save(im, fp, [("gif", (0,0)+im.size, 0, RAWMODE[im.mode])])
  384.  
  385.         fp.write("\0") # end of image data
  386.  
  387.     finally:
  388.         del im.encoderinfo
  389.  
  390.     return fp.data
  391.  
  392.  
  393. # --------------------------------------------------------------------
  394. # Registry
  395.  
  396. Image.register_open(GifImageFile.format, GifImageFile, _accept)
  397. Image.register_save(GifImageFile.format, _save)
  398. Image.register_extension(GifImageFile.format, ".gif")
  399. Image.register_mime(GifImageFile.format, "image/gif")
  400.  
  401. #
  402. # Uncomment the following line if you wish to use NETPBM/PBMPLUS
  403. # instead of the built-in "uncompressed" GIF encoder
  404.  
  405. # Image.register_save(GifImageFile.format, _save_netpbm)
  406.